import {
  _decorator,
  Component,
  Prefab,
  instantiate,
  UITransform,
  Vec3,
} from "cc";
import { Cell } from "./Cell";
import { CellType, Exits } from "./Types";
import { LEVEL_DATA } from "./LevelData";
const { ccclass, property } = _decorator;

@ccclass("GridContainer")
export class GridContainer extends Component {
  @property(Prefab)
  cellPrefab: Prefab = null;

  private _grid: Cell[][] = [];
  public CELL_SIZE = 128;

  public initGrid(currentLevel: number) {
    const levelConfig = LEVEL_DATA[currentLevel];
    const startX = -(levelConfig.gridSize * this.CELL_SIZE) / 2;
    const startY = -(levelConfig.gridSize * this.CELL_SIZE) / 2;

    for (let y = 0; y < levelConfig.gridSize; y++) {
      this._grid[y] = [];
      for (let x = 0; x < levelConfig.gridSize; x++) {
        const cellData = levelConfig.cells[y][x];
        const cell = instantiate(this.cellPrefab);
        const cellComp = cell.getComponent(Cell);

        const posX = startX + x * this.CELL_SIZE + this.CELL_SIZE / 2;
        const posY = startY + y * this.CELL_SIZE + this.CELL_SIZE / 2;

        cellComp.setPosition(x, y);
        cell.setPosition(posX, posY, 0);

        this._grid[y][x] = cellComp;
        this.node.addChild(cell);

        this.scheduleOnce(() => {
          cellComp.setExits([
            cellData.exits["up"],
            cellData.exits["right"],
            cellData.exits["down"],
            cellData.exits["left"],
          ]);
          cellComp.setType(cellData.type);
        });
      }
    }
  }

  public clearGrid() {
    for (let row of this._grid) {
      for (let cell of row) {
        cell.node.destroy();
      }
    }
    this._grid = [];
  }

  public worldToGrid(worldPos: Vec3): { x: number; y: number } {
    const gridTransform = this.node.getComponent(UITransform);
    const localPos = gridTransform.convertToNodeSpaceAR(worldPos);
    const currentLevel = this.getCurrentLevelFromGridSize();
    const gridOffset = (LEVEL_DATA[currentLevel].gridSize * this.CELL_SIZE) / 2;

    return {
      x: Math.floor((localPos.x + gridOffset) / this.CELL_SIZE),
      y: Math.floor((localPos.y + gridOffset) / this.CELL_SIZE),
    };
  }

  public gridToWorld(gridX: number, gridY: number): Vec3 {
    const currentLevel = this.getCurrentLevelFromGridSize();
    const gridOffset = (LEVEL_DATA[currentLevel].gridSize * this.CELL_SIZE) / 2;

    const localX = gridX * this.CELL_SIZE - gridOffset;
    const localY = gridY * this.CELL_SIZE - gridOffset;
    const localPos = new Vec3(localX, localY, 0);

    const gridTransform = this.node.getComponent(UITransform);
    return gridTransform.convertToWorldSpaceAR(localPos);
  }

  public isValidGridPosition(x: number, y: number): boolean {
    return (
      x >= 0 && x < this._grid[0].length && y >= 0 && y < this._grid.length
    );
  }

  public getCellType(x: number, y: number): CellType {
    return this._grid[y][x].getType();
  }

  public setCellTypeAndExits(
    x: number,
    y: number,
    type: CellType,
    exits: boolean[]
  ) {
    if (!this.isValidGridPosition(x, y)) {
      return;
    }

    if (!this._grid[y] || !this._grid[y][x]) {
      return;
    }

    const cell = this._grid[y][x];
    cell.setExits(exits);
    cell.setType(type);
  }

  public validatePath(): boolean {
    let startCell: Cell = null;
    let endCell: Cell = null;
    let startPos = { x: 0, y: 0 };
    let endPos = { x: 0, y: 0 };

    for (let y = 0; y < this._grid.length; y++) {
      for (let x = 0; x < this._grid[y].length; x++) {
        const cell = this._grid[y][x];
        if (cell.getType() === CellType.Character) {
          startCell = cell;
          startPos = { x, y };
        } else if (cell.getType() === CellType.Home) {
          endCell = cell;
          endPos = { x, y };
        }
      }
    }

    if (!startCell || !endCell) {
      return false;
    }

    const visited: boolean[][] = Array(this._grid.length)
      .fill(false)
      .map(() => Array(this._grid[0].length).fill(false));

    return this.dfs(startPos.x, startPos.y, visited);
  }

  private dfs(x: number, y: number, visited: boolean[][]): boolean {
    if (y < 0 || y >= this._grid.length || x < 0 || x >= this._grid[0].length) {
      return false;
    }

    if (visited[y][x]) {
      return false;
    }

    const currentCell = this._grid[y][x];

    if (currentCell.getType() === CellType.Home) {
      return true;
    }

    visited[y][x] = true;

    const directions = [
      { dir: "up", dx: 0, dy: 1 },
      { dir: "right", dx: 1, dy: 0 },
      { dir: "down", dx: 0, dy: -1 },
      { dir: "left", dx: -1, dy: 0 },
    ];

    for (const { dir, dx, dy } of directions) {
      if (!currentCell.hasExit(dir)) {
        continue;
      }

      const nextX = x + dx;
      const nextY = y + dy;

      if (!this.isValidGridPosition(nextX, nextY)) {
        continue;
      }

      const nextCell = this._grid[nextY][nextX];

      if (
        nextCell.getType() !== CellType.Road &&
        nextCell.getType() !== CellType.Home
      ) {
        continue;
      }

      const oppositeDir = this.getOppositeDirection(dir);
      if (!nextCell.hasExit(oppositeDir)) {
        continue;
      }

      if (this.dfs(nextX, nextY, visited)) {
        return true;
      }
    }

    return false;
  }

  private getOppositeDirection(dir: string): string {
    switch (dir) {
      case "up":
        return "down";
      case "down":
        return "up";
      case "left":
        return "right";
      case "right":
        return "left";
      default:
        return dir;
    }
  }

  private getCurrentLevelFromGridSize(): number {
    // 通过网格大小反推当前关卡
    const gridSize = this._grid.length;
    return LEVEL_DATA.findIndex((level) => level.gridSize === gridSize);
  }
}
